
/*
 * Copyright (C) 2008-2009 Archie L. Cobbs. All rights reserved.
 *
 * $Id: IndexTest1.java 377 2010-03-11 20:50:18Z archie.cobbs $
 */

package org.dellroad.sidekar;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.dellroad.sidekar.annotation.SidekarEntity;
import org.dellroad.sidekar.annotation.SidekarField;
import org.dellroad.sidekar.annotation.SidekarIndex;
import org.dellroad.sidekar.util.VoidDatabaseAction;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertSame;

public class IndexTest1 extends DatabaseTest {

    private Person fred;
    private Person barney;
    private Person wilma;
    private Person betty;

    @Test
    public void testIndex1() {

        // Populate
        this.db.performTransaction(null, new VoidDatabaseAction() {
            public void executeVoid() {
                populateDB();
            }
        });

        // Verify
        this.db.performTransaction(null, new VoidDatabaseAction() {
            public void executeVoid() {
                verifyDB();
            }
        });

        // Export
        compareExport();
    }

    protected void populateDB() {
        this.fred = createPerson("fred", 10);
        this.barney = createPerson("barney", 10);
        this.betty = createPerson("betty", 10);
        this.wilma = createPerson("wilma", 20);
    }

    protected void verifyDB() {

        // Check simple one-field index
        Index<Person> byName = this.db.getEntity(Person.class).getIndex("byName");
        checkResults(byName.iterator(), this.barney, this.betty, this.fred, this.wilma);
        assertSame(byName.getFirst("fred"), this.fred);
        assertSame(byName.getFirst("barney"), this.barney);
        assertSame(byName.getFirst("betty"), this.betty);
        assertSame(byName.getFirst("wilma"), this.wilma);
        assertNull(byName.getFirst("nonexistent"));

        // Check multi-field index
        Index<Person> byAgeAndName = this.db.getEntity(Person.class).getIndex("byAgeAndName");
        checkResults(byAgeAndName.iterator(), this.barney, this.betty, this.fred, this.wilma);
        checkResults(byAgeAndName.iterator(10), this.barney, this.betty, this.fred);
        checkResults(byAgeAndName.iterator(10, "fred"), this.fred);
        checkResults(byAgeAndName.iterator(10, "barney"), this.barney);
        checkResults(byAgeAndName.iterator(10, "betty"), this.betty);
        checkResults(byAgeAndName.iterator(10, "wilma"));
        checkResults(byAgeAndName.iterator(10, "nobody"));
        checkResults(byAgeAndName.iterator(20), this.wilma);
        checkResults(byAgeAndName.iterator(20, "fred"));
        checkResults(byAgeAndName.iterator(20, "barney"));
        checkResults(byAgeAndName.iterator(20, "betty"));
        checkResults(byAgeAndName.iterator(20, "wilma"), this.wilma);
        checkResults(byAgeAndName.iterator(20, "nobody"));
        checkResults(byAgeAndName.iterator(30));
        this.barney.setAge(20);
        checkResults(byAgeAndName.iterator(), this.betty, this.fred, this.barney, this.wilma);
        checkResults(byAgeAndName.iterator(10), this.betty, this.fred);
        checkResults(byAgeAndName.iterator(10, "fred"), this.fred);
        checkResults(byAgeAndName.iterator(10, "barney"));
        checkResults(byAgeAndName.iterator(10, "betty"), this.betty);
        checkResults(byAgeAndName.iterator(10, "wilma"));
        checkResults(byAgeAndName.iterator(10, "nobody"));
        checkResults(byAgeAndName.iterator(20), this.barney, this.wilma);
        checkResults(byAgeAndName.iterator(20, "fred"));
        checkResults(byAgeAndName.iterator(20, "betty"));
        checkResults(byAgeAndName.iterator(20, "barney"), this.barney);
        checkResults(byAgeAndName.iterator(20, "wilma"), this.wilma);
        checkResults(byAgeAndName.iterator(20, "nobody"));
        checkResults(byAgeAndName.iterator(30));
    }

    protected void checkResults(Iterator<Person> i, Person... people) {
        ArrayList<Person> actual = new ArrayList<Person>();
        while (i.hasNext())
            actual.add(i.next());
        ArrayList<Person> expected = new ArrayList<Person>();
        for (Person person : people)
            expected.add(person);
        assertEquals((Object)actual, (Object)expected);
    }

    protected Person createPerson(String name, int age) {
        Person person = this.db.create(Person.class);
        person.setName(name);
        person.setAge(age);
        return person;
    }

    @Override
    protected List<Class<?>> getSchemaClasses() {
        return Arrays.<Class<?>>asList(Person.class);
    }

    // Model classes

    @SidekarEntity(indexes = {
      @SidekarIndex(name = "byName", fields = "name"),
      @SidekarIndex(name = "byAgeAndName", fields = { "age", "name" })
    })
    public abstract static class Person implements Item {

        @SidekarField
        public abstract String getName();
        public abstract void setName(String name);

        @SidekarField
        public abstract int getAge();
        public abstract void setAge(int age);

        @Override
        public String toString() {
            return "Person[name=" + getName() + ",age=" + getAge() + "]";
        }
    }
}

